﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using System.Collections.ObjectModel;
using System.Data;
using AutoTestingFramework.Core;

namespace AutoTestingFramework.Core.Controls
{
    public class TableWebElement : IWebElement
    {
        IWebElement m_Table;
        string m_RowXPath = "";
        string m_HeaderXPath = "";
        bool m_ConsiderFirstTRasHeader = false;
        /// <summary>
        /// Setting this to true, will result in Header propery returning first tr tag. 
        /// If tbody tag is present, then first row from tbody tag.
        /// </summary>
        public bool ConsiderFirstTRasHeader
        {
            get { return m_ConsiderFirstTRasHeader; }
            set { m_ConsiderFirstTRasHeader = value; }
        }
        IReadOnlyCollection<RowWebElement> m_Rows;

        string Id
        {
            get { return GetAttribute("id"); }
        }
        #region Base Methods, Not changed
        public void Clear()
        {
            m_Table.Clear();
        }

        public void Click()
        {
            m_Table.Click();
        }

        public bool Displayed
        {
            get { return m_Table.Displayed; }
        }

        public bool Enabled
        {
            get { return m_Table.Enabled; }
        }

        public string GetAttribute(string attributeName)
        {
            return m_Table.GetAttribute(attributeName);
        }

        public string GetCssValue(string propertyName)
        {
            return m_Table.GetAttribute(propertyName);
        }

        public System.Drawing.Point Location
        {
            get { return m_Table.Location; }
        }

        public bool Selected
        {
            get { return m_Table.Selected; }
        }

        public void SendKeys(string text)
        {
            m_Table.SendKeys(text);
        }

        public System.Drawing.Size Size
        {
            get { return m_Table.Size; }
        }

        public void Submit()
        {
            m_Table.Submit();
        }

        public string TagName
        {
            get { return m_Table.TagName; }
        }

        public string Text
        {
            get { return m_Table.Text; }
        }

        public IWebElement FindElement(By by)
        {
            return m_Table.FindElement(by);
        }

        public System.Collections.ObjectModel.ReadOnlyCollection<IWebElement> FindElements(By by)
        {
            return m_Table.FindElements(by);
        }
        #endregion
        public TableWebElement(IWebElement Table)
        {
            if (Table.TagName.ToUpper() != "TABLE")
                throw new Exception("Invalid Element '" + Table.TagName + "'. Only table element can be initialized");
            m_Table = Table;


        }
        public IReadOnlyCollection<RowWebElement> Rows
        {
            get
            {
                if (m_Rows == null)
                {
                    IReadOnlyCollection<IWebElement> _IRows;
                    if (m_RowXPath != "")
                    {
                        try
                        {
                            _IRows = FindElements(By.XPath(m_RowXPath));


                        }
                        catch (Exception ex)
                        {
                            _IRows = null;
                            m_Rows = null;
                            return null;
                        }
                    }
                    else
                    {
                        try
                        {

                            //  _IRows = FindElements(By.XPath("//*[@id='" + Id + "']/tbody/tr"));
                            _IRows = FindElements(By.XPath("./tbody/tr"));

                        }
                        catch (Exception ex)
                        {
                            try
                            {
                                //_IRows = FindElements(By.XPath("//*[@id='" + Id + "']/tr"));
                                _IRows = FindElements(By.XPath("./tr"));


                            }
                            catch (Exception exInner)
                            {
                                m_Rows = null;
                                _IRows = null;
                                return null;
                            }
                        }

                    }
                    var List = from RE in _IRows
                               select new RowWebElement(RE);

                    m_Rows = new ReadOnlyCollection<RowWebElement>(List.ToList<RowWebElement>());
                    _IRows = null;
                }
                return m_Rows;
            }
        }
        /// <summary>
        /// Returns row at a particular index, inside tbody tag if present, or first tr tag located.
        /// </summary>
        /// <param name="RowIndex">1 based row index</param>
        /// <returns></returns>
        public RowWebElement RowAt(int RowIndex)
        {
            string indexPath = "[" + RowIndex + "]";
            IWebElement row = null;
            if (m_RowXPath != "")
            {
                try
                {
                    row = FindElement(By.XPath(m_RowXPath + indexPath));


                }
                catch (Exception ex)
                {

                    return null;
                }
            }
            else
            {
                try
                {
                    if (Id == null)
                        row = FindElement(By.XPath("./tbody/tr" + indexPath));
                    else
                        row = FindElement(By.XPath("//*[@id='" + Id + "']/tbody/tr" + indexPath));

                }
                catch (Exception ex)
                {
                    try
                    {
                        if (Id == null)
                            row = FindElement(By.XPath("./tr" + indexPath));
                        else
                            row = FindElement(By.XPath("//*[@id='" + Id + "']/tr" + indexPath));

                    }
                    catch (Exception exInner)
                    {

                        return null;
                    }
                }

            }
            return new RowWebElement(row);

        }

        public RowWebElement Header
        {
            get
            {
                if (m_ConsiderFirstTRasHeader)
                {
                    return RowAt(1);
                }
                if (m_HeaderXPath != "")
                {
                    try
                    {
                        return new RowWebElement(FindElement(By.XPath(m_HeaderXPath)));


                    }
                    catch (Exception ex)
                    {

                        return null;
                    }
                }
                else
                {
                    try
                    {

                        //  _IRows = FindElements(By.XPath("//*[@id='" + Id + "']/tbody/tr"));
                        return new RowWebElement(FindElement(By.XPath("./thead/th")));
                    }
                    catch (Exception ex)
                    {
                        try
                        {
                            //_IRows = FindElements(By.XPath("//*[@id='" + Id + "']/tr"));
                            return new RowWebElement(FindElement(By.XPath("./th")));


                        }
                        catch (Exception exInner)
                        {
                            return null;
                        }
                    }

                }
            }
        }
        public bool MatchTableData(DataTable dtToMatch, string ignoreCellText, bool ignoreInVisibleItems = false)
        {
            try
            {
                int rowIteration = 0;

                foreach (RowWebElement row in Rows)
                {
                    if (!row.Displayed && ignoreInVisibleItems)
                        continue;
                    DataRow dataRow = dtToMatch.Rows[rowIteration++];
                    int colIteration = 0;
                    foreach (CellWebElement cell in row.Cells)
                    {
                        if ((!cell.Displayed && ignoreInVisibleItems) || ignoreCellText == dataRow[colIteration].ToString())
                        {
                            colIteration++;
                            continue;
                        }
                        if (MatchCellData(cell, null, dataRow[colIteration++].ToString()))
                            continue;
                        else
                            return false;
                    }

                }

                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }


        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="dtToMatch"></param>
        /// <param name="searchItemsBy">Element to use for matching within the td/th. Relative search is done with respect to  the cell being matched </param>
        /// <param name="ignoreInVisibleItems">Ignore the cell which is not displayed.</param>
        /// <returns></returns>
        public bool MatchTableData(DataTable dtToMatch, string ignoreCellText, By[] searchItemsBy, bool ignoreInVisibleItems = false)
        {
            try
            {
                int rowIteration = 0;

                foreach (RowWebElement row in Rows)
                {
                    if (!row.Displayed && ignoreInVisibleItems)
                        continue;
                    DataRow dataRow = dtToMatch.Rows[rowIteration++];
                    int colIteration = 0;
                    foreach (CellWebElement cell in row.Cells)
                    {
                        if ((!cell.Displayed && ignoreInVisibleItems) || ignoreCellText == dataRow[colIteration].ToString())
                        {
                            colIteration++;
                            continue;
                        }
                        if (MatchCellData(cell, searchItemsBy[colIteration], dataRow[colIteration++].ToString()))
                            continue;
                        else
                            return false;
                    }

                }

                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }



        }

        public bool MatchTableData(DataTable dtToMatch, string ignoreCellText, CellMatchMetaData[] arrayofCMD, Func<string, string[]> fetchData, bool ignoreInVisibleItems = false)
        {
            try
            {
                int rowIteration = 0;

                foreach (RowWebElement row in Rows)
                {
                    if (!row.Displayed && ignoreInVisibleItems)
                        continue;
                    DataRow dr = dtToMatch.Rows[rowIteration++];
                    int colIteration = 0;
                    bool isMatching = true;
                    foreach (CellWebElement cell in row.Cells)
                    {
                        if ((!cell.Displayed && ignoreInVisibleItems) || ignoreCellText == dr[colIteration].ToString())
                        {
                            colIteration++;
                            continue;
                        }
                        if (arrayofCMD[colIteration] == null)
                        {
                            isMatching = MatchCellData(cell, null, dr[colIteration++].ToString());
                        }
                        else
                            isMatching = MatchCellData(cell, arrayofCMD[colIteration], fetchData, dr[colIteration++].ToString(), ignoreCellText);
                        if (isMatching)
                            continue;
                        else
                            return false;
                    }

                }

                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }



        }
        bool MatchCellData(CellWebElement cellWebElement, By locator, string data, string propertyToMatch = "")
        {
            IWebElement ElementToMatch = cellWebElement;
            if (locator != null)
            {
                ElementToMatch = cellWebElement.FindElement(locator);
            }

            switch (propertyToMatch.ToUpper())
            {
                case "":
                case "TEXT":
                    return cellWebElement.Text == data;

            }
            return false;
        }

        bool MatchCellData(CellWebElement cellWebElement, CellMatchMetaData matchDataMetaData, Func<string, string[]> fetchData, string cellData, string ignoreControlText)
        {
            IWebElement elementToMatch = cellWebElement;
            string[] dataArray = fetchData(cellData);
            int elementIterator = 0;

            foreach (ControlMatchMetaData CMD in matchDataMetaData.ControlMatchMDs)
            {
                if (dataArray[elementIterator] == ignoreControlText)
                    continue;
                elementToMatch = cellWebElement.FindElement(CMD.Locator);
                string valueToMatch = "";
                string[] attValue = CMD.PropertyToMatch.Split('#');
                switch (attValue[0].ToUpper())
                {
                    case "":
                    case "TEXT":
                        valueToMatch = elementToMatch.Text;
                        break;
                        ;
                    case "SELECTED":
                        valueToMatch = elementToMatch.Selected.ToString();
                        break;
                    case "ATTRIBUTE":
                        valueToMatch = elementToMatch.GetAttribute(attValue[1]);
                        break;
                }

                if (valueToMatch == dataArray[elementIterator++])
                    continue;
                else
                    return false;
            }
            return true;
        }




    }
}
